"""
人脸识别引擎封装
基于 insightface 提供人脸检测、特征提取、比对功能
"""
from typing import List, Optional, Tuple
import numpy as np
import cv2
from insightface.app import FaceAnalysis
from config.settings import settings
from utils.logger import app_logger
from models.business_models import FaceFeatureDto, FaceDetectDto


class FaceEngine:
    """人脸识别引擎（单例模式）"""

    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        if self._initialized:
            return

        try:
            app_logger.info("初始化人脸识别引擎...")
            
            # 确定模型路径
            model_root = settings.face_model_root if settings.face_model_root else None
            if model_root:
                import os
                # 转换为绝对路径
                model_root = os.path.abspath(model_root)
                app_logger.info(f"使用本地模型路径: {model_root}")
            else:
                app_logger.info("使用默认模型路径: ~/.insightface")

            # 初始化 insightface
            self.app = FaceAnalysis(
                name=settings.face_model_name,
                root=model_root,  # 指定模型根目录
                providers=['CPUExecutionProvider']  # 使用CPU，如需GPU改为CUDAExecutionProvider
            )

            # 准备模型
            self.app.prepare(
                ctx_id=0,
                det_size=settings.face_det_size_tuple
            )

            self.det_thresh = settings.face_det_thresh
            self.min_face_size = settings.min_face_size

            app_logger.info("人脸识别引擎初始化完成")
            self._initialized = True

        except Exception as e:
            app_logger.error(f"人脸识别引擎初始化失败: {e}", exc_info=True)
            raise

    def detect_faces(self, img: np.ndarray) -> List[FaceDetectDto]:
        """
        检测人脸

        Args:
            img: OpenCV图像数组

        Returns:
            人脸检测结果列表
        """
        try:
            # 检测人脸
            faces = self.app.get(img)

            if not faces:
                return []

            # 转换为DTO
            results = []
            for face in faces:
                # 人脸框
                bbox = face.bbox.astype(int)
                face_rect = {
                    'x': int(bbox[0]),
                    'y': int(bbox[1]),
                    'width': int(bbox[2] - bbox[0]),
                    'height': int(bbox[3] - bbox[1])
                }

                # 关键点
                landmarks = face.kps.tolist() if hasattr(face, 'kps') else []

                # 质量评分（使用检测得分）
                quality = float(face.det_score) if hasattr(face, 'det_score') else 1.0

                # 人脸角度（如果有）
                angle = None
                if hasattr(face, 'pose'):
                    angle = float(np.linalg.norm(face.pose))

                result = FaceDetectDto(
                    face_rect=face_rect,
                    landmarks=landmarks,
                    quality=quality,
                    angle=angle
                )
                results.append(result)

            app_logger.debug(f"检测到 {len(results)} 张人脸")
            return results

        except Exception as e:
            app_logger.error(f"人脸检测失败: {e}", exc_info=True)
            return []

    def extract_feature(self, img: np.ndarray) -> Optional[FaceFeatureDto]:
        """
        提取人脸特征（仅处理第一张人脸）

        Args:
            img: OpenCV图像数组

        Returns:
            人脸特征DTO，失败返回None
        """
        try:
            # 检测人脸
            faces = self.app.get(img)

            if not faces:
                app_logger.warning("未检测到人脸")
                return None

            if len(faces) > 1:
                app_logger.warning(f"检测到多张人脸({len(faces)})，仅处理第一张")

            # 取第一张人脸
            face = faces[0]

            # 特征向量
            feature_vector = face.embedding  # 512维

            # 人脸框
            bbox = face.bbox.astype(int)
            face_rect = {
                'x': int(bbox[0]),
                'y': int(bbox[1]),
                'width': int(bbox[2] - bbox[0]),
                'height': int(bbox[3] - bbox[1])
            }

            # 检查人脸大小
            if face_rect['width'] < self.min_face_size or face_rect['height'] < self.min_face_size:
                app_logger.warning(f"人脸尺寸过小: {face_rect['width']}x{face_rect['height']}")
                return None

            # 关键点
            landmarks = face.kps.tolist() if hasattr(face, 'kps') else []

            # 质量评分
            quality = float(face.det_score) if hasattr(face, 'det_score') else 1.0

            return FaceFeatureDto(
                feature_vector=feature_vector,
                quality=quality,
                face_rect=face_rect,
                landmarks=landmarks
            )

        except Exception as e:
            app_logger.error(f"特征提取失败: {e}", exc_info=True)
            return None

    def compare_features(
        self,
        feature1: np.ndarray,
        feature2: np.ndarray
    ) -> float:
        """
        比对两个特征向量

        Args:
            feature1: 特征向量1
            feature2: 特征向量2

        Returns:
            相似度分数 0-1
        """
        try:
            # L2归一化
            feature1_norm = feature1 / np.linalg.norm(feature1)
            feature2_norm = feature2 / np.linalg.norm(feature2)

            # 计算余弦相似度（归一化向量的点积）
            similarity = np.dot(feature1_norm, feature2_norm)

            # 确保在0-1范围内
            similarity = float(np.clip(similarity, 0, 1))

            return similarity

        except Exception as e:
            app_logger.error(f"特征比对失败: {e}", exc_info=True)
            return 0.0

    def validate_face_quality(
        self,
        img: np.ndarray,
        min_quality: float = 0.5
    ) -> Tuple[bool, str]:
        """
        验证人脸质量

        Args:
            img: 图像
            min_quality: 最低质量要求

        Returns:
            (是否合格, 错误信息)
        """
        try:
            faces = self.app.get(img)

            if not faces:
                return False, "未检测到人脸"

            if len(faces) > 1:
                return False, f"检测到多张人脸({len(faces)})，请确保只有一张人脸"

            face = faces[0]

            # 检查人脸大小
            bbox = face.bbox.astype(int)
            width = bbox[2] - bbox[0]
            height = bbox[3] - bbox[1]

            if width < self.min_face_size or height < self.min_face_size:
                return False, f"人脸尺寸过小: {width}x{height}, 最小要求{self.min_face_size}x{self.min_face_size}"

            # 检查质量分数
            quality = float(face.det_score) if hasattr(face, 'det_score') else 1.0
            if quality < min_quality:
                return False, f"人脸质量不合格: {quality:.2f} < {min_quality}"

            return True, ""

        except Exception as e:
            app_logger.error(f"质量验证失败: {e}", exc_info=True)
            return False, f"质量验证异常: {str(e)}"


# 全局单例实例
face_engine = FaceEngine()
